#include "case.h"




int fd;
int fd_udp;
int flag = 1;
struct sockaddr_in targetmem;//准备接受的结构体
struct sockaddr_in srvaddr;
struct sockaddr_in udp_addr;
struct fd_ip_t *info;


PND list_init(void)
{
    PND phead = (PND)malloc(sizeof(ND));
    if (phead == NULL)
    {
        printf("init fail\n");
        return NULL;
    }
    phead -> next = NULL;
    return phead;
    
}


PND make_node(int n,char * ipmsg)
{
    PND pnew = (PND)malloc(sizeof(ND));
    if (pnew == NULL)
    {
        printf("make node fail\n");
        return NULL;
    }
    pnew -> next = NULL;
    strcpy(pnew->ip,ipmsg);
    pnew -> fd_msg = n;
    return pnew;
}


bool insert_node(PND phead,PND pnew)
{
    if (phead == NULL||pnew == NULL)
    {
        return false;
    }
    PND p = phead;
    while(p->next != NULL)
    {
        p = p->next;
    }
    p->next = pnew;
    pnew->next = NULL;
    return true;
    
}


void broadcast(struct sockaddr_in msg,int fdnum,struct sockaddr_in ip)
{
        
    char ipmsg[16] = {0};
    inet_ntop(AF_INET,&ip.sin_addr.s_addr,ipmsg,16);
    socklen_t len = sizeof(struct sockaddr_in);
    //发送数据给udp服务器（事先得知udp服务器地址）
	char buf[100];
    bzero(buf,sizeof(buf));
    sprintf(buf,"welcome %s join us.",ipmsg);
	sendto(fd_udp,buf,strlen(buf),0,(struct sockaddr *)&msg,len);
	
}


void recv_file(PND pnode)
{
    //同上结构体接收来自客服机的文件名及文件信息  
    char a[50] = {0};
    struct file_info filerecv;
    bzero(&filerecv,sizeof(filerecv));
    PND parr = pnode -> next;
    int m;
    recv(parr->fd_msg,&a,sizeof(a),0);
    //接收信息
    while(1)
    {    
        //recv(parr->fd_msg,&a,sizeof(a),0);
        m = recv(parr->fd_msg,&filerecv.filename,sizeof(filerecv),0);
        if(m == 0)
        {
            printf("IP:%s并未传输文件\n",parr->ip);
            parr = parr -> next;
        }
        else if(m != 0)
        {
            printf("%s\n",filerecv.filename);
            break;
        }

    }
    
    
    FILE * fp_op = fopen(filerecv.filename,"w+");
    if (fp_op == NULL)
    {
        perror("fopen fail");
        exit(1);
    }

    //第二次接收文件的内容
    char info[1024];
    int mem;
    while(1)
    {
        bzero(info,sizeof(info));
        mem = recv(parr->fd_msg,info,sizeof(info),0);
        if (mem == 0)
        {
            printf("接收成功\n");
            break;
        }
        fwrite(info,1024,1,fp_op);
        
    }


    fclose(fp_op);
}


void *msg_recv(void * arg)
{
    struct fd_ip_t *p = (struct fd_ip_t *)arg;
    char buf[100];
	while(1)
	{	
		int n;
		bzero(buf,100);
		if(( n=recv(p->fd_info,buf,100,0) ) == 0)
			break;
		else if(n > 0)
		{
			char ip_buf[16];
			bzero(ip_buf,16);
			inet_ntop(AF_INET,&(p->ip.sin_addr),ip_buf,16);
            if (flag != 0)
            {
                printf("client %s say:%s\n",ip_buf,buf);
			    printf("port:%hu\n",ntohs(p->ip.sin_port));	
            }
            else if(flag == 0)
            {
                continue;
            }
		
		}
		else
			perror("recv fail");
        
	}
	printf("break while\n");
	//close(p->fd);
	pthread_exit(NULL);
}


void *accept_client(void * arg)
{
    PND p = (PND)arg;

    while(1)
    {
        bzero(&targetmem,sizeof(targetmem));//清空
        int len = sizeof(targetmem);
        char ipbuf[16] = {0};
        int fd_new = accept(fd,(struct sockaddr*)&targetmem,&len);//接收连接的客服机
        if(fd_new > 0)
        {
                
            inet_ntop(AF_INET,&targetmem.sin_addr.s_addr,ipbuf,16);
            printf("new connecting ip is:%s\t%hu\n",ipbuf,ntohs(targetmem.sin_port));//接收成功后打印其ip和端口
            printf("fd:%d\n",fd_new);

            

            PND pnew = make_node(fd_new,ipbuf);
            insert_node(p,pnew);
           
            info = (struct fd_ip_t *)malloc(sizeof(struct fd_ip_t));
            info->fd_info = fd_new;
            memcpy((void *)&info->ip,(void *)&targetmem,sizeof(targetmem));

            broadcast(srvaddr,fd_udp,targetmem);

            //接收信息
            pthread_t tid;
            int ret = pthread_create(&tid,NULL,msg_recv,(void *)info);
            if(ret != 0)
            {
                printf("creat fail.\n");
                exit(1);
            }
      

        }
    }
}


void show_mem(PND phead)
{
    PND ptmp = phead->next;
    while(ptmp != NULL)
    {
        printf("The connecting User's IP is:%s\tfd:%d\n",ptmp->ip,ptmp->fd_msg);
        ptmp = ptmp -> next;
    }
}


int chatting(PND pnode)
{
    //发送信息
    
    char sbuf[100];
    char ipbuf[16];
    
    while(1)
    {
        bzero(ipbuf,sizeof(ipbuf));
        bzero(sbuf,100);
		scanf("%s",sbuf);//输入要发送的内容
        getchar();
        if (strcmp(sbuf,"quit") == 0)
        {
            return 0;
        }
        printf("Enter the target ip:");
		scanf("%s",ipbuf);
        getchar();
        PND ptmp = pnode -> next;
		while(1)
        {
            char * a = (char *)ptmp->ip;
            if (strcmp(a,ipbuf) == 0)
            {
                send(ptmp->fd_msg,sbuf,strlen(sbuf),0);//把buf中的数据发出去，0表示普通的正常操作 
                printf("发送成功.\n");
                break;
            }
            ptmp = ptmp -> next;
            if (ptmp == NULL)
            {
                printf("Can't find the ip.\n");
                break;
            }
            
        }
        
    }

}


void send_file(PND pnode)
{		
	FILE *fp;//定义文件指针

	char buf[100];//存文本内容
	char name[50];//存文件名
	char b[20];//存放IP
	bzero(buf,100);
	bzero(name,50);
	bzero(b,20);

	printf("请输入需要传送的文件(带路径)\n");
	scanf("%s",name);
	getchar();
	
	fp=fopen(name,"r");//打开文件
	fread(buf,10,10,fp);//读取文本内容

	printf("输入需要发送到的客户机的IP：\n");
	scanf("%s",b); 	
	getchar();	

	PND p;
	p = pnode->next;
	while(strcmp(b,p->ip)!=0)
	{	
		if (p->next==NULL)
		{
			printf("最后一个\n" );
			break;
		}
		p=p->next;
	}
	 //通过IP寻找对应套接字
	 
	send(p->fd_msg,name,50,0);//发送文件名
	sleep(5);
	send(p->fd_msg,buf,100,0);//发送文本内容
	

}


int main(int argc, char *argv[])
{
    //使用规则
    if (argc != 2)
    {
        printf("Using rule:%s\tport\n",argv[0]);
    }

    PND phead = list_init();
    if (phead == NULL)
    {
        printf("creat fail\n");
        exit(1);
    }
    
//////////////////////////////////////TCP//////////////////////////////////////
    fd = socket(AF_INET,SOCK_STREAM,0);//创建套接字
    int uu = 1;
    setsockopt(fd,SOL_SOCKET, SO_REUSEADDR,&uu,sizeof(uu));
    
    struct sockaddr_in server;//定义结构体
    bzero(&server,sizeof(server));//清空结构体
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    server.sin_family = AF_INET;
    server.sin_port = htons(atoi(argv[1]));

    //绑定
    int ret = bind(fd,(struct sockaddr*)&server,sizeof(server));
    if (ret < 0)
    {
        perror("bind fail");
    }

    listen(fd,3);//监听 最大连接数为3+4
///////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////UDP//////////////////////////////////////
    //创建数据报式套接字（用于UDP）
	fd_udp = socket(AF_INET,SOCK_DGRAM,0);

    //设置套接字的属性，SO_BROADCAST 允许发送广播数据
	int on = 1;
	setsockopt(fd_udp,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
    

	srvaddr.sin_family = AF_INET;
	srvaddr.sin_port = htons(atoi(argv[1]));//把小端存储的端口号转换成网络传输需要的大端
    
    
    inet_pton(AF_INET,"192.168.23.255",&srvaddr.sin_addr.s_addr);

///////////////////////////////////////////////////////////////////////////////



    pthread_t tid1;
    int a = pthread_create(&tid1,NULL,accept_client,(void*)phead);
    if (a != 0)
    {
        printf("Fail to creat tid1\n");
        exit(1);
    }       



    while(1)
    {
      
        int choice;
        printf("==========================快乐聊天室==========================\n");
        printf("========================1.显示在线用户========================\n");
        printf("========================2.选择用户私聊========================\n");
        printf("========================3.恢复在线模式========================\n");
        printf("========================4.进入勿扰模式========================\n");
        printf("=======================5.文件接受(未开发)=====================\n");
        printf("==========================6.文件发送==========================\n");
        printf("=========================7.退出聊天室=========================\n");
        printf("==============================================================\n");

        scanf("%d",&choice);
        getchar();
        switch (choice)
        {
        case 1:
            show_mem(phead);
            break;

        case 2:
            chatting(phead);
        
        case 3:
            flag = 1;
            break;

        case 4:
            flag = 0;
            break;

        case 5:
            recv_file(phead);
            break;  

        case 6:
            send_file(phead); 
            break;

        case 7:
            exit(1); 
        default:
            break;
        }
    }

    return 0;
}